package com.neusoft.databus.agent.pool.snmp;

import java.util.Map;
import java.util.NoSuchElementException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.neusoft.databus.common.connection.AbstractConnection;
import com.neusoft.databus.common.connection.SnmpConnection;
import com.neusoft.databus.common.exception.RetryableException;
import com.neusoft.databus.common.pool.Connector;

import lombok.extern.slf4j.Slf4j;

/**
 * SNMP连接器
 *
 * @author naxiang
 */
@Slf4j
@Component
public class SnmpConnector implements Connector<SnmpWrapper> {
    @Autowired
    private SnmpPool pool;

    final Map<String, ReadWriteLock> lockMap = new ConcurrentHashMap<>();

    @Override
    public SnmpWrapper getConn(final AbstractConnection connection) {
        return this.getConn(connection, 0);

    }

    @Override
    public SnmpWrapper getConn(final AbstractConnection connection, final int timeout) {

        final SnmpConnection snmpConnection = (SnmpConnection) connection;
        final String lockId =
                snmpConnection.getIpAddress() + ":" + snmpConnection.getPort() + ":" + snmpConnection.getCommunity()
                        + ":"
                        + snmpConnection.getSeq();

        SnmpWrapper snmpWrapper = null;
        try {
            synchronized (SnmpConnector.class) {
                ReadWriteLock lock = this.lockMap.get(lockId);
                if (null == lock) {
                    lock = new ReentrantReadWriteLock(true);
                    this.lockMap.put(lockId, lock);
                }
            }
            final ReadWriteLock lock = this.lockMap.get(lockId);
            lock.writeLock().lock();
            if (timeout == 0) {
                snmpWrapper = this.pool.borrowObject(snmpConnection);
            } else {
                snmpWrapper = this.pool.borrowObject(snmpConnection, timeout * 1000);
            }
        } catch (final NoSuchElementException e) {
            throw new RetryableException(e.getMessage(), e);
        } catch (final Exception e) {
            // 通过对象池Factory创建新对象时，抛出的所有异常，都会在这里捕捉到。
            // 但是无法判断连接是否中断
            throw new RetryableException(e.getMessage(), e);
        } finally {
            final ReadWriteLock lock = this.lockMap.get(lockId);
            lock.writeLock().unlock();
        }
        return snmpWrapper;
    }

    @Override
    public void returnConn(final AbstractConnection connection, final SnmpWrapper conn) {
        try {
            this.pool.returnObject((SnmpConnection) connection, conn);
        } catch (final Exception e) {
            SnmpConnector.log.warn("归还Snmp对象时错误, {}", e.getMessage());
        }
    }

    @Override
    @Deprecated
    public void invalidateConn(final AbstractConnection connection, final SnmpWrapper conn) {
        if (null == conn) {
            return;
        }
        try {
            this.pool.invalidateObject((SnmpConnection) connection, conn);
        } catch (final Exception e) {
            SnmpConnector.log.error(e.getMessage(), e);
        }
    }

    @Override
    @Deprecated
    public void removeAllConns(final AbstractConnection connection) {
        try {
            this.pool.clear((SnmpConnection) connection);
        } catch (final Exception e) {
            SnmpConnector.log.error(e.getMessage(), e);
        }

    }
}
